﻿//==============================================================================  
//Copyright (C) 2012-2015 9UN.ORG. All rights reserved. 
//GUID：46172595-6b69-4526-b9da-426d663f5b05
//CLR Version: 4.0.30319.18033
//Code Author：Kevin Wang
//Contact：Email(Admin@9un.org),QQ(265382 or 74344)
//Filename：DoubleWindowBase
//Namespace：RuanRang.Media.Core
//Functions：DoubleWindowBase  
//Created by Kevin Wang at 2013/2/2 23:05:09 http://blog.9un.org
//============================================================================== 

#region Usings

using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

#endregion

namespace RuanRang.Media.Windows.Interop
{
    /// <summary>
    /// Represents a custom windows messages pump.
    /// </summary>
    public class WindowsMessagePump : IDisposable
    {
        //private static readonly ILog logger = LogManager.GetLogger(typeof (WindowsMessagePump).FullName);
        private readonly ManualResetEvent _eventStop = new ManualResetEvent(false);
        private readonly StringBuilder _builder = new StringBuilder(256);
        private bool _run;
        private IntPtr _windowHandle;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public WindowsMessagePump()
        {
            _windowHandle = IntPtr.Zero;
            initialize();
        }

        /// <summary>
        /// Constructor with window specified.
        /// </summary>
        public WindowsMessagePump(IntPtr window)
        {
            _windowHandle = window;
            initialize();
        }

        /// <summary>
        /// Handle of window.
        /// </summary>
        public IntPtr WindowHandle
        {
            get
            {
                return (_windowHandle);
            }
            set
            {
                if (_run)
                {
                    throw new InvalidOperationException("Cannot set window handle of pump when message pump in RUN state.");
                }
                _windowHandle = value;
            }
        }

        #region IDisposable Members

        /// <summary>
        /// Clean up resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
        }

        #endregion

        private void initialize()
        {
        }

        /// <summary>
        /// Runs the pump.
        /// </summary>
        public void Run()
        {
            _run = true;
            _eventStop.Reset();
            DispatchRoutine();
        }

        /// <summary>
        /// Stops the pump.
        /// </summary>
        public void Stop()
        {
            _run = false;
            _eventStop.Set();
        }

        /// <summary>
        /// Dispatches the routine.
        /// </summary>
        private void DispatchRoutine()
        {
            //
            var msg = new WindowMessage();
            while (_run)
            {
                var array = new IntPtr[1];
                array[0] = _eventStop.SafeWaitHandle.DangerousGetHandle();
                //
                var result = NativeMethods.MsgWaitForMultipleObjectsEx(1, array, -1,
                     NativeMethods.MsgWaitEventFlags.QS_ALLEVENTS |
                     NativeMethods.MsgWaitEventFlags.QS_ALLPOSTMESSAGE |
                     NativeMethods.MsgWaitEventFlags.QS_SENDMESSAGE |
                     NativeMethods.MsgWaitEventFlags.QS_EVENT |
                     NativeMethods.MsgWaitEventFlags.QS_KEY |
                     NativeMethods.MsgWaitEventFlags.QS_PAINT |
                     NativeMethods.MsgWaitEventFlags.QS_POSTMESSAGE,
                     NativeMethods.MsgWaitFlags.WaitAny);
                if (!_run)
                {
                    return;
                }
                if (result == 0)
                {
                    break;
                }
                if (result != 1) continue;
                if (!_run)
                {
                    return;
                }
                result = NativeMethods.GetMessageW(ref msg, _windowHandle, 0, 0);
                //
                /*logger.Info("Message received. Window: 0x{0:x}({1}) ; Message Id: 0x{2:x}({3}) ", (int)msg.windowHandle, GetWindowText(msg.windowHandle),
                                msg.messageCode,
                                Enum.GetName(typeof(NativeMethods.WindowsMessages), (NativeMethods.WindowsMessages)msg.messageCode));*/
                //
                switch (result)
                {
                    case -1:
                        return;
                    case 0:
                        return;
                }
                if (!FilterWindowMessage(ref msg))
                {
                    NativeMethods.TranslateMessage(ref msg);
                    NativeMethods.DispatchMessage(ref msg);
                }
                else
                {
                    //logger.Info("Message filtered and not dispatched.");
                }
            }
        }

        /// <summary>
        /// Returns window's text by the API function.
        /// </summary>
        /// <param name="handle">The handle.</param>
        /// <returns>System.String.</returns>
        public string GetWindowText(IntPtr handle)
        {
            GetWindowTextW(handle, _builder, _builder.Capacity);
            return (_builder.ToString());
        }

        /// <summary>
        /// Win32 thunk to original unicode function.
        /// </summary>
        /// <param name="hWnd">The h WND.</param>
        /// <param name="lpString">The lp string.</param>
        /// <param name="nMaxCount">The n max count.</param>
        /// <returns>System.Int32.</returns>
        [DllImport("user32.dll")]
        public static extern int GetWindowTextW(IntPtr hWnd,
                                                [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 2)] StringBuilder lpString,
                                                int nMaxCount
            );

        /// <summary>
        /// FilterWindowMessage overridable method.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
        protected internal virtual bool FilterWindowMessage(ref WindowMessage message)
        {
            return (false);
        }

        /// <summary>
        /// Finalizer.
        /// </summary>
        ~WindowsMessagePump()
        {
            Dispose(false);
        }

        /// <summary>
        /// Clean up.
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            Stop();
            _eventStop.Close();
            if (disposing)
            {
                GC.SuppressFinalize(this);
            }
        }
    }
}